package com.abo.yaas.webservice;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import com.abo.yaas.domain.YaasPojo;
import com.abo.yaas.webservice.constants.URLConstants;
import com.abo.yaas.webservice.exceptions.InvalidCredentialsException;
import com.abo.yaas.webservice.exceptions.NotFoundException;
import com.abo.yaas.webservice.exceptions.WebServiceException;
import com.abo.yaas.webservice.hibernate.SortCriteria;
import com.abo.yaas.webservice.hibernate.WSFilter;
import com.abo.yaas.webservice.messageproviders.ClassWrapper;
import com.abo.yaas.webservice.messageproviders.ListWrapper;
import com.abo.yaas.webservice.utils.AuthenticationUtil;
import com.sun.jersey.core.util.Base64;

public class EntityResource<T extends YaasPojo> {
    @Provider
    public static class InvalidCredentialsExceptionMapper implements ExceptionMapper<InvalidCredentialsException> {
        @Override
        public Response toResponse(InvalidCredentialsException exception) {
            return Response.serverError().entity(exception.getMessage()).build();
        }
    }

    @Provider
    public static class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
        @Override
        public Response toResponse(NotFoundException exception) {
            return Response.serverError().entity(exception.getMessage()).build();
        }
    }

    @Provider
    public static class WebServiceExceptionMapper implements ExceptionMapper<WebServiceException> {
        @Override
        public Response toResponse(WebServiceException exception) {
            return Response.serverError().entity(exception.getMessage()).build();
        }
    }

    private final Class<T> clazz;

    EntityResource(Class<T> classType) {
        this.clazz = classType;
    }

    @GET
    @Path(URLConstants.containsId + "/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Boolean> containsEntity(@PathParam("id") Long id, @Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        return CriteriaQueryUtil.containsEntity(id, filter, clazz);
    }

    protected WSFilter<T> extractFilter(final UriInfo ui) {
        return new WSFilter<T>();
    }

    protected List<SortCriteria<T>> extractSortCriteria(final UriInfo ui) {
        final ArrayList<SortCriteria<T>> result = new ArrayList<SortCriteria<T>>();
        result.add(new SortCriteria<T>("id", true, clazz));
        return result;
    }

    @GET
    @Produces(MediaType.TEXT_XML)
    @Path(URLConstants.getById + "/{id}")
    public T getEntity(@PathParam("id") Long id) {
        if (id == null) {
            throw new WebServiceException("Id is not specified!");
        }
        final T result = CriteriaQueryUtil.getEntity(id, clazz);
        return result;
    }

    @GET
    @Path(URLConstants.entityCount)
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Long> getEntityCount(@Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        return CriteriaQueryUtil.getEntityCount(filter, clazz);
    }

    @GET
    @Path(URLConstants.entityId + "/{idx}")
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Object> getEntityIdAt(@PathParam("idx") Integer idx, @Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getEntityIdAt(idx, filter, sortCriteria, clazz);
    }

    @GET
    @Path(URLConstants.idList)
    @Produces(MediaType.APPLICATION_XML)
    public ListWrapper<Object> getEntityIdList(@Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getEntityIdList(filter, sortCriteria, clazz);
    }

    @GET
    @Path(URLConstants.firstEntityId)
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Object> getFirstEntityId(@Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getFirstEntityId(filter, sortCriteria, clazz);
    }

    @GET
    @Produces(MediaType.APPLICATION_XML)
    @Path(URLConstants.lastEntityId)
    public ClassWrapper<Object> getLastEntityId(@Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getLastEntityId(filter, sortCriteria, clazz);
    }

    @GET
    @Path(URLConstants.nextEntityId + "/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Object> getNextEntityId(@PathParam("id") Long id, @Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getNextEntityId(id, filter, sortCriteria, clazz);
    }

    protected String getPassword(HttpHeaders headers) {
        if (headers == null || headers.getRequestHeader("authorization").get(0) == null) {
            return "";
        }
        String auth = headers.getRequestHeader("authorization").get(0);
        auth = auth.substring("Basic ".length());
        final String[] values = new String(Base64.base64Decode(auth)).split(":");
        return values.length < 2 ? "" : values[1];
    }

    @GET
    @Path(URLConstants.previousEntityId + "/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public ClassWrapper<Object> getPreviousEntityId(@PathParam("id") Long id, @Context UriInfo ui) {
        final WSFilter<T> filter = extractFilter(ui);
        final List<SortCriteria<T>> sortCriteria = extractSortCriteria(ui);
        return CriteriaQueryUtil.getPreviousEntityId(id, filter, sortCriteria, clazz);
    }

    protected String getUser(HttpHeaders headers) {
        if (headers == null || headers.getRequestHeader("authorization").get(0) == null) {
            return "";
        }
        String auth = headers.getRequestHeader("authorization").get(0);
        auth = auth.substring("Basic ".length());
        final String[] values = new String(Base64.base64Decode(auth)).split(":");
        return values.length < 1 ? "" : values[0];
    }

    @POST
    @Produces(MediaType.APPLICATION_XML)
    public T saveOrUpdateEntity(final T entity, @Context HttpHeaders headers) throws InvalidCredentialsException {
        AuthenticationUtil.authenticate(getUser(headers), getPassword(headers));
        return CriteriaQueryUtil.saveEntity(entity);
    }
}
